package edu.siu.math.egut.main;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

import com.sun.tools.javac.util.List;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.io.LiE;
import edu.siu.math.egut.io.OutputPref;
import edu.siu.math.egut.util.Polynomial;
import edu.siu.math.egut.util.PolynomialReadException;
import edu.siu.math.egut.util.Sunipotent;
import edu.siu.math.egut.util.Unipotent;
/**
 * DCA stands for Double Coset Analysis.  The set up is this.  Let G be a split connected  
 * simply connected algebraic group.  For EGUT, we assume G is simple and of one of the 
 * five exceptional types, but the theory here is general.  As always, we assume G to be equipped with 
 * a choice of split maximal torus T (used to define roots) Borel subgroup B=TN (used to define 
 * simple roots and positive roots, as well as standard parabolics) and a system of 
 * epinglage, chosen so that the structure constants match the algorithm in the paper of 
 * Gilkey and Seitz, with the numbering used in LiE (which is different from that of Gilkey-Seitz
 * in the case of G2).
 * 
 * We consider two parabolics, P=MU and Q=LV, and a psi-factor psi on U.  Let us assume that psi
 * is in fact a character of U (though no checking of this is performed).  Let S < M denote the 
 * stabilizer of psi. The purpose of this program is to help with the analysis of the (U, psi) 
 * Fourier coefficient of an Eisenstein series induced from the parabolic Q.  This Fourier 
 * coefficient breaks naturally into terms indexed by Q\G/SU.  Our goal is to analyze these 
 * terms.
 * 
 * As a first approximation, we may consider Q\G, which is represented by minimal-length elements
 * of the Weyl group.  Experience indicates that only the term coming from the long element will 
 * be nonzero.  The present program is intended to help prove this. 
 * 
 *
 * 
 * Once the relevant data is extracted from the args, the following steps are performed for each nu0.
 * 
 * (1) Display nu0.
 * (2) Compute and display the set of positive roots made negative by nu0.  (By definition, they 
 * will all be roots of T in M.)  This can be computed using the EGUT LiE initfile command "goesDown( nu0)."
 * (3) Compute  the set of roots of T in U left positive by w0nu0, and display a side-by-side comparison
 * of these roots and the roots that they are mapped to, with those mapped into L shown first and those
 * mapped into V shown second.  The set may be computed using the EGUT LiE initfile command "goesUp( w0^nu0, U)."
 * It is also image( inverse(nu0), goesUp( w0, U)).  Comparisons may be obtained using the "compare" 
 * command, after intersecting with L and V respectively.
 * (4) Compute and display the set of roots of T in U made negative by w0nu0. This may be computed using the 
 * EGUT LiE initfile commands "goesDown( w0^nu0,  U)," "image( inverse( nu0), goesDown( w0))," or 
 * complement( goesDown( w0^nu0), goesDown( nu0))," reflecting two alternate descriptions of the same set:  it 
 * is the image under nu0^{-1} of the set of positive roots made negative by w0, and it is the set of roots 
 * made negative by w0nu0, but not by nu0. 
 * (5) Compute and display a certain character (or more general psi-factor if the original psi was not a character)
 *  \psi_delta(u') of U' := U\cap (w_0nu0)^{-1} N w_0nu0 (which is the product of the one-dimensional unipotent subgroups 
 *  indexed by the set of roots computed in (3)).  This character is defined in terms of an element delta of N_nu0 
 *  (which is the product of the one-dimensional unipotent subgroups indexed by the set of roots computed in (2)).
 *  To be precise delta^{-1} u' \delta is in U and \psi_\delta(u') = \psi( delta^{-1} u' \delta).
 * 
 * @author Joseph Hundley
 *
 */
public class DCA3 {
    private static final OutputPref ERR = OutputPref.ERR;
    private static final OutputPref SHOW=OutputPref.SHOW;
    private static final OutputPref HIDE = OutputPref.HIDE;
    private static LiE lie = new LiE();
    private static FileWriter texFile;

    /**
     * @param args parabolic P, parabolic Q, polynomial psi, parabolic P2 of the Levi of P, name of exceptional group 
     */
    public static void main(String[] args) {
	
	
	// If we don't have the right number of args, print an error message and exit.
	if( args.length <5){
	    System.out.print("Not enough information.  Need:\n " +
	    		"\t* An exceptional group"+
	    		"\t* Parabolic P, such that the Fourier coefficient is taken along the\n\tunipotent radical of P,\n" +
	    		"\t* Parabolic Q, such that Eisenstein is induced from Q,\n" +
	    		"\t* a polynomial psi factor,\n" +
	    		"\t* A file name.  The corresponding file should contain a LiE matrix.\n" +
	    		"Example: E8 2 1 u11222221+u22343210 1234321 \n");
	    System.exit(1);
	    
	}
	
	
	// extract necessary data from args.  
	String pString =args[1];
	String qString = args[2];
	
	Polynomial psi = Polynomial.create(1);
	    try {
		psi = Polynomial.read(args[3]);
	    } catch (PolynomialReadException e1) {
		System.out.println("Problem reading psi.");
		e1.printStackTrace();
		System.exit(1);
	    }
	String groupName = args[0];
	
	String mString="";
	
	try {
	    mString = Algorithms.fileToString(args[4]);
	} catch (FileNotFoundException e1) {
	    say("Couldn't find file "+args[4]);
	    Scanner scanner = new Scanner(System.in);
	    mString = Algorithms.getFileName(scanner);
	}
	
	// mString should be a LiE-format matrix.  The next step is to 
	// split it up and access the individual rows.  
	
	// split on "],["
	String[] rowStrings = mString.split("\\],\\[");
	// first one will be of the format "[[d1,d2,...,dr".  remove "[["
	rowStrings[0] = rowStrings[0].substring(2);
	// last one will be of the format "d1,...,dr]]".  remove "]]"
	rowStrings[rowStrings.length-1] = 
	    rowStrings[rowStrings.length-1].substring(0,rowStrings[rowStrings.length-1].length()-2);
	// now all are format "d1,...,dr".  Add "[" to the beginning and "]" to the end.
	for( int i = 0 ; i < rowStrings.length; i++)
	    rowStrings[i] = "["+rowStrings[i]+"]";
	
	try{
	// try first default file name.
	File file = new File(args[4].substring(0, args[4].lastIndexOf('.'))+".tex");
	if(file.exists()){ // try second default file name 
	    file = new File(args[4].substring(0, args[4].lastIndexOf('.'))+"-TeXfile.tex");
	    if( file.exists()){ // get name from user.
		Scanner scanner = new Scanner( System.in);
		say("Neither default file name is available.  Please input a file name manually.");
		String name="";
		while( file.exists()){
		    name = scanner.next();
		
		    file = new File(name);
		    if( file.exists())
			say("That's taken too.");
		}
		texFile = new FileWriter(name);
	    }else 
		texFile = new FileWriter(args[4].substring(0, args[4].lastIndexOf('.'))+"-TeXfile.tex");
	}else 
	    texFile = new FileWriter(args[4].substring(0, args[4].lastIndexOf('.'))+".tex");
	
	texFile.write(Algorithms.TEX_HEADER);
	}catch( IOException e){
	    e.printStackTrace();
	}
	
	    
	try {
	    
	    
	    
		// set default group   
	    lie.exec( "setdefault "+groupName, ERR);
	    lie.exec("p = "+pString, ERR);
	    lie.exec("q="+qString, ERR);
	    
	    for(int i=0; i < rowStrings.length; i++){
	    // get sigma
	    lie.exec("sigma = W_word("+rowStrings[i]+")", ERR);
	    
	    // get w0 (the P-reduced part of sigma) and nu (the rest of sigma).
	    lie.exec("w0 = lr_reduce(complement("+qString+"),sigma, complement("+pString+"))", ERR);
	    lie.exec("nu=reduce(inverse(w0)^sigma)", ERR);
	    String nuString=lie.exec("nu", HIDE);
	    texFile.write("\\subsection{$\\nu=["+Algorithms.removeWhiteSpaceCommasAndBrackets(nuString)+"]$}\n\n");
	    
		analyze( nuString, pString, qString, psi, lie);
		
	    }// end for i = 0 to rowStrings.length-- i.e., end of iteration over rows...
		
		texFile.write(Algorithms.TEX_FOOTER);
		texFile.close();
		

	    

	    
	} catch (IOException e) {
	    System.out.println("Problem starting LiE.");
	    e.printStackTrace();
	    System.exit(1);
	} catch (Exception e) {
	    System.out.println("Exception encountered.  Possible cause:  problem reading LiE output.");

	    e.printStackTrace();
	}

	
    }

    
    /**
     * @param nu0String defining the Weyl element nu0
     * @param pString defining the parabolic P
     * @param qString defining the parabolic Q
     * @param psi defining the character psi
     * @param lie a LiE process
     * @throws IOException usually as a result of calling lie.exec
     * @throws Exception various "read" commands
     */
    public static void analyze( String nu0String, String pString, String qString, Polynomial psi, LiE lie )  throws IOException, Exception{
	
	// define  nu0 in the LiE process
	lie.exec("nu0 = "+nu0String, ERR);

	    // display goesDown( nu0), and collect it into an int[][]
	String goesDown = lie.exec("goesDown( nu0) ", HIDE);
	    texFile.write("$$\\Phi(N_\\nu, T) ="+(goesDown.contains("null")?"\\emptyset":"\\left\\{\\begin{matrix}"+Algorithms.makeRootList(goesDown)+"\\end{matrix}\\right\\}")+"$$\n");
	
	// for convenience, define U.  We call it u in LiE because 'U' is not allowed by LiE.
	lie.exec("u = unipRad("+pString+")", ERR);
	
	// compute goesUp( w0^nu0, u), and collect it into an int[][].  do not 
	// display:  we wish to display more precise information using compare.
	lie.exec("u1 = goesUp( w0^nu0, u)", ERR);
	lie.exec("u2 = goesDown(w0^nu0, u)", ERR);
	int[][] uRoots = Algorithms.readLiEMatrix(lie.exec("u1^u2", HIDE));
	int[][] uPrimeRoots = Algorithms.readLiEMatrix(lie.exec("u1", HIDE));
	
	
	if( !goesDown.contains("null")){
	    int[][] deltaRoots = Algorithms.readLiEMatrix(goesDown);
		
	Unipotent delta  = new Unipotent(deltaRoots, "\\delta"); 
	Unipotent u = new Unipotent(uRoots, "u");
	
	//multiply u by d on the left 
	Unipotent v = Unipotent.multiply(delta, u);

	// make a copy of psi, so that we can manipulate psi and then calculate the difference.
	Polynomial psi1= psi;

	
	
	/* 
	 * Push delta past u.  
	 */
	int count = 1;
	while( v.indexOf(deltaRoots)!= -1){
	    
	    try{
	    v.pushFarRightAndRemove(v.lastIndexOf(deltaRoots));
	    }
	    catch(Exception e){
		System.out.println(v);
		System.out.println("index of delta roots: " +v.indexOf(deltaRoots)+ "\tSubsequent index of uPrimeRoots: "+ v.indexOf(uPrimeRoots, v.indexOf(deltaRoots))+ "\tLength: " + v.getNumberOfTerms());
		System.exit(1);
	    }
	    
	    v.cleanUp();
	    v.clearZeros();

	    psi = v.chOfVars(psi);
	    count++;
	    
	    
	}// end while u.indexOf... != -1

	
	// display \delta \cdot \psi_U
	texFile.write("$$\\delta \\cdot \\psi_U = \\psi\\begin{pmatrix}"+psi+"\\end{pmatrix}$$\n");
	
	// compute the difference \psi_u^{-1}(\delta \cdot \psi_U)
	Polynomial diff = psi.minus(psi1);
		
	// Extract the collection of roots alpha such that u_alpha appears in psi.  	
	java.util.List<Sunipotent> sunipotentsWithVariablesThatAppear =new ArrayList<Sunipotent>();
		for( Sunipotent s : u.getTerms()){
		    if( diff.dependsOn(u.stringOf(s)))
			sunipotentsWithVariablesThatAppear.add(s);
		    
		}
		int[][] rootsWithVariablesThatAppear = new int[sunipotentsWithVariablesThatAppear.size()][v.getGroup().getRank()];
		for( int i=0; i < sunipotentsWithVariablesThatAppear.size(); i++)
		    rootsWithVariablesThatAppear[i]=sunipotentsWithVariablesThatAppear.get(i).getRoot();

		
		
		
		//compute images of these roots under sigma := w0nu0
		// and  display results
		StringBuilder sb = new StringBuilder("$$");
		int count2 = 0;
		for( int i = 0; i < rootsWithVariablesThatAppear.length; i++){
		    if(count2%4==0&&count2>0)
			sb.append("$$\n$$");
		    count2++;
		    sb.append("\\sigma\\cdot ");
		    sb.append(Algorithms.tupleToShortString(rootsWithVariablesThatAppear[i]));
		    sb.append("=");
		    sb.append(Algorithms.shortenLiERoot(
			    lie.exec("image(w0^nu0,"+ Algorithms.tupleToString(rootsWithVariablesThatAppear[i]) +")",HIDE)
			                                 ));
		    if(i < rootsWithVariablesThatAppear.length-1)
			sb.append(",\\;");
		}// end for i 
		sb.append("$$\n");
		texFile.write(sb.toString());
		
		String down = lie.exec("goesDown(w0^nu0,"+Algorithms.writeLieMatrix(rootsWithVariablesThatAppear)+")", HIDE);
		
		if(down.contains("null"))
		    texFile.write("All positive.  Case Complete.\n\n");
		else
		    texFile.write("Roots sent down: $"+Algorithms.makeRootList(down)+"$\n\n");

		
	}// end if !goesDown contains "null"  (so this next bit is what happens if nu was the identity...)
	else 
	    texFile.write("$$\\delta \\cdot \\psi_U = \\psi_U$$\n\n");
	
	
    }
    
    /**
     * Just to avoid typing System.out.println over and over...
     * @param string String
     */
    public static void say(final String string) {
	System.out.println(string);
    }

 
    private static String lieVectorString(int[] is) {
	StringBuilder sb = new StringBuilder("[");
	for(int i = 0 ; i <is.length; i++){
	    sb.append(Integer.toString(is[i]));
	    if( i < is.length-1)
		sb.append(",");
	    
	    
	}
	sb.append("]");
	
	return sb.toString();
    }

    /**
     * This method facilitates interaction with LiE by supplying the letter (E, G, or F) which 
     * appears in the name of the unique exceptional group of the given rank.
     * 
     * type(2) = G
     * type(4) = F
     * type(6,7, or 8) = E
     * type( any other number ) = null.
     * 
     * @param rank an integer:  should be 2,4,6,7, or 8.
     * @return the letter in the name of the unique excpetional group of the given rank
     */
    public static String type(final int rank) {
	switch (rank){
	case 2: return "G";
	case 4: return "F";
	case 6: return "E";
	case 7: return "E";
	case 8: return "E";
	default: return null;
	}
	    
    
    }

}
